/* -------------------------------------------------------------------------
 *
 * pgstat_wal.c
 *	  WAL统计的实现。
 *
 * 本文件包含WAL统计的实现。它与pgstat.c保持分开，以便区分统计访问/
 * 存储实现与个别统计类型的详细信息。
 *
 * Copyright (c) 2001-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *	  src/backend/utils/activity/pgstat_wal.c
 * -------------------------------------------------------------------------
 */

#include "postgres.h"

#include "utils/pgstat_internal.h"
#include "executor/instrument.h"


PgStat_WalStats PendingWalStats = {0};

/*
 * 从上一次调用 pgstat_report_wal() 时保存的 pgWALUsage 的 WAL 使用计数器。用于通过从当前计数器中减去上一个计数器来计算 pgstat_report_wal() 调用之间发生了多少 WAL 使用。
 */
static WalUsage prevWalUsage;


/*
 * 计算 WAL 使用计数器增加了多少，并更新共享统计信息。
 *
 * 必须由生成 WAL 的进程调用，这些进程不调用 pgstat_report_stat()，例如 walwriter。
 *
 * 将 "force" 设置为 true 确保统计信息被刷新；请注意，这需要获取 pgstat shmem LWLock，并在上面等待。当设置为 false 时，如果无法获取锁，则可能不会刷新统计信息。
 */
void pgstat_report_wal(bool fc_force)
{
	bool		fc_nowait;

	/* 像在 pgstat.c 中一样，当 !force 时，不要等待锁获取 */
	fc_nowait = !fc_force;

	/* 刷新 wal 统计信息 */
	pgstat_flush_wal(fc_nowait);
}

/*
 * SQL 可调用 pgstat* 函数的支持函数。返回指向 WAL 统计结构的指针。
 */
PgStat_WalStats *
pgstat_fetch_stat_wal(void)
{
	pgstat_snapshot_fixed(PGSTAT_KIND_WAL);

	return &pgStatLocal.snapshot.wal;
}

/*
 * 通过从当前计数器中减去上一个计数器，计算 WAL 使用计数器增加了多少。
 *
 * 如果 nowait 为 true，则当无法获取锁时，该函数返回 true。否则返回 false。
 */
bool pgstat_flush_wal(bool fc_nowait)
{
	PgStatShared_Wal *fc_stats_shmem = &pgStatLocal.shmem->wal;
	WalUsage	fc_diff = {0};

	Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
	Assert(pgStatLocal.shmem != NULL &&
		   !pgStatLocal.shmem->is_shutdown);

	/*
	 * 即使什么都没有发生，也可以调用此函数。在这种情况下避免为无谓的事情获取锁。
	 */
	if (!pgstat_have_pending_wal())
		return false;

	/*
	 * 我们在其他地方不会更新本地 WalStats 的 WAL 使用部分。
	 * 通过从当前计数器中减去上一个计数器，计算 WAL 使用计数器增加了多少。
	 */
	WalUsageAccumDiff(&fc_diff, &pgWalUsage, &prevWalUsage);
	PendingWalStats.wal_records = fc_diff.wal_records;
	PendingWalStats.wal_fpi = fc_diff.wal_fpi;
	PendingWalStats.wal_bytes = fc_diff.wal_bytes;

	if (!fc_nowait)
		LWLockAcquire(&fc_stats_shmem->lock, LW_EXCLUSIVE);
	else if (!LWLockConditionalAcquire(&fc_stats_shmem->lock, LW_EXCLUSIVE))
		return true;

#define WALSTAT_ACC(fld) fc_stats_shmem->stats.fld += PendingWalStats.fld
	WALSTAT_ACC(wal_records);
	WALSTAT_ACC(wal_fpi);
	WALSTAT_ACC(wal_bytes);
	WALSTAT_ACC(wal_buffers_full);
	WALSTAT_ACC(wal_write);
	WALSTAT_ACC(wal_sync);
	WALSTAT_ACC(wal_write_time);
	WALSTAT_ACC(wal_sync_time);
#undef WALSTAT_ACC

	LWLockRelease(&fc_stats_shmem->lock);

	/*
	 * 保存当前计数器以便后续计算 WAL 使用。
	 */
	prevWalUsage = pgWalUsage;

	/*
	 * 清除统计缓冲区，以便可以重复使用。
	 */
	MemSet(&PendingWalStats, 0, sizeof(PendingWalStats));

	return false;
}

void pgstat_init_wal(void)
{
	/*
	 * 用 pgWalUsage 初始化 prevWalUsage，以便 pgstat_flush_wal() 可以通过从 pgWalUsage 中减去 prevWalUsage 来计算 pgWalUsage 计数器增加了多少。
	 */
	prevWalUsage = pgWalUsage;
}

/*
 * 为了确定自上次以来是否发生了任何 WAL 活动，不仅需要检查生成的 WAL 记录数量，还需要检查 WAL 写入和同步的数量。因为即使是没有生成 WAL 记录的事务，在刷新数据页面时也可以写入或同步 WAL 数据。
 */
bool pgstat_have_pending_wal(void)
{
	return pgWalUsage.wal_records != prevWalUsage.wal_records ||
		PendingWalStats.wal_write != 0 ||
		PendingWalStats.wal_sync != 0;
}

void pgstat_wal_reset_all_cb(TimestampTz fc_ts)
{
	PgStatShared_Wal *fc_stats_shmem = &pgStatLocal.shmem->wal;

	LWLockAcquire(&fc_stats_shmem->lock, LW_EXCLUSIVE);
	memset(&fc_stats_shmem->stats, 0, sizeof(fc_stats_shmem->stats));
	fc_stats_shmem->stats.stat_reset_timestamp = fc_ts;
	LWLockRelease(&fc_stats_shmem->lock);
}

void pgstat_wal_snapshot_cb(void)
{
	PgStatShared_Wal *fc_stats_shmem = &pgStatLocal.shmem->wal;

	LWLockAcquire(&fc_stats_shmem->lock, LW_SHARED);
	memcpy(&pgStatLocal.snapshot.wal, &fc_stats_shmem->stats,
		   sizeof(pgStatLocal.snapshot.wal));
	LWLockRelease(&fc_stats_shmem->lock);
}
